﻿using System;
using System.IO;
using FFmpeg.AutoGen;
using MediaInfoLib;
using System.Text.Json;

namespace FFmpegVideoClip.Lib
{
    public class GetMediaInfo
    {
        public record struct MediaInfoResult(TimeSpan Duration, int Height = 0, int Width = 0, bool HasDuration = false, bool IsVideoStream = false, bool IsAudioStream = false, bool IsSubStream = false, bool ForceValidFile = false)
        {
            public static readonly MediaInfoResult Empty = new MediaInfoResult(TimeSpan.Zero);
            public bool IsValidFile => HasDuration && IsVideoStream | IsAudioStream;
            public bool IsCompriseForceValidFile => ForceValidFile || IsValidFile;
            override public string ToString() => $"{{Duration:{Duration},Height:{Height},Width:{Width},HasDuration:{HasDuration},IsVideoStream:{IsVideoStream},IsAudioStream:{IsAudioStream},IsSubStream:{IsSubStream},IsValidFile:{IsValidFile},ForceValidFile:{ForceValidFile},IsCompriseForceValidFile:{IsCompriseForceValidFile}}}";
        }
        private static AVRational AV_TIME_BASE_Q = new AVRational { num = 1, den = ffmpeg.AV_TIME_BASE };
        private unsafe static MediaInfoResult GetFFMpegInfoResult(string FilePath)
        {
            AVFormatContext* _pFormatContext = null;
            try{
                ffmpeg.avformat_open_input(&_pFormatContext, FilePath, null, null).ThrowExceptionIfError();
                ffmpeg.avformat_find_stream_info(_pFormatContext, null).ThrowExceptionIfError();
                TimeSpan ResultTime = TimeSpan.FromMicroseconds(_pFormatContext->duration);
                AVStream* stream;
                bool IsVideo = false, IsAudio = false, IsSub = false;
                int Height = 0, Width = 0;
                for (int i = 0; i < _pFormatContext->nb_streams; i++)
                {
                    stream = _pFormatContext->streams[i];
                    if (stream->codecpar->codec_type == AVMediaType.AVMEDIA_TYPE_VIDEO)
                    {
                        IsVideo = true;
                        Width = stream->codecpar->width;
                        Height = stream->codecpar->height;
                    }
                    else if (stream->codecpar->codec_type == AVMediaType.AVMEDIA_TYPE_AUDIO)
                        IsAudio = true;
                    else if (stream->codecpar->codec_type == AVMediaType.AVMEDIA_TYPE_SUBTITLE)
                        IsSub = true;
                }
                return new(ResultTime, Height, Width, ResultTime != TimeSpan.Zero, IsVideo, IsAudio, IsSub);
            }finally{if (_pFormatContext != null) ffmpeg.avformat_close_input(&_pFormatContext);}
        }

        private static MediaInfoResult GetMediaInfoResult_Json(string FilePath)
        {
            using (MediaInfo mi = new())
            {
                mi.Open(FilePath);
                mi.Option("Inform", "JSON");
                mi.Option("Complete", "1");
                MediaInfoData? data = JsonSerializer.Deserialize<MediaInfoData>(mi.Inform() ?? string.Empty, ClipManage.SerializerOptions);
                if (data != null)
                {
                    LogManage.WriteLineNewInfo($"MediaInfo:{data}");
                    return new(TimeSpan.FromSeconds(data.Duration), data.Height, data.Width, data.Duration > 0, data.VideoCount > 0, data.AudioCount > 0, data.TextCount > 0);
                }
                LogManage.WriteLineNewInfo("MediaInfo失败:");
                return MediaInfoResult.Empty;
            }
        }
        private static MediaInfoResult GetMediaInfoResult(string FilePath)
        {
            bool IsVideo = true, IsAudio = true, IsSub = true, HasDuration = true;
            double doubleValue = 0d;
            int IntValue, Height = 0, Width = 0;

            using (MediaInfo mi = new())
            {
                mi.Open(FilePath);
                string? GetResult = mi.Get(StreamKind.General, 0, GeneralQueryItems.Duration);
                if (string.IsNullOrEmpty(GetResult) || !double.TryParse(GetResult, out doubleValue))
                    HasDuration = false;

                GetResult = mi.Get(StreamKind.General, 0, GeneralQueryItems.VideoCount);
                if (string.IsNullOrEmpty(GetResult) || !int.TryParse(GetResult, out IntValue) || IntValue < 0)
                    IsVideo = false;

                GetResult = mi.Get(StreamKind.General, 0, GeneralQueryItems.AudioCount);
                if (string.IsNullOrEmpty(GetResult) || !int.TryParse(GetResult, out IntValue) || IntValue < 0)
                    IsAudio = false;

                GetResult = mi.Get(StreamKind.General, 0, GeneralQueryItems.TextCount);
                if (string.IsNullOrEmpty(GetResult) || !int.TryParse(GetResult, out IntValue) || IntValue < 0)
                    IsSub = false;

                
                GetResult = mi.Get(StreamKind.Video, 0, VideoQueryItems.Height_Original);
                if (string.IsNullOrEmpty(GetResult) || !int.TryParse(GetResult, out Height) || Height < 1)
                {
                    GetResult = mi.Get(StreamKind.Video, 0, VideoQueryItems.Height);
                    if (!string.IsNullOrEmpty(GetResult))
                        _ = int.TryParse(GetResult, out Height);
                }

                
                GetResult = mi.Get(StreamKind.Video, 0, VideoQueryItems.Width_Original);
                if (string.IsNullOrEmpty(GetResult) || !int.TryParse(GetResult, out Width) || Width < 1)
                {   
                    GetResult = mi.Get(StreamKind.Video, 0, VideoQueryItems.Width);
                    if (!string.IsNullOrEmpty(GetResult))
                        _ = int.TryParse(GetResult, out Width);
                }
            }
            MediaInfoResult result = new(TimeSpan.FromMilliseconds(doubleValue), Height, Width, HasDuration, IsVideo, IsAudio, IsSub);
            LogManage.WriteLineNewInfo($"GetMediaInfoResult:{result}");
            return result;
        }
        public static MediaInfoResult GetInfo(string FilePath)
        {
            if (!File.Exists(FilePath))
                return new();
            try
            {
                MediaInfoResult result = GetMediaInfoResult(FilePath);
                if (result.IsValidFile)
                {
                    LogManage.WriteLineNewInfo($"MediaInfoResult:{result}");
                    return result;
                }
                result = GetFFMpegInfoResult(FilePath);
                return result;
            }
            catch (Exception ex)
            {
                LogManage.WriteExceptionInfo($"获取文件[{FilePath}信息失败,请确认是否支持该数据格式]", ex);
                return new();
            }
        }

    }
}
